package com.iwdnb.gkgz.application.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Lists;
import com.iwdnb.bmnf.common.dingtalk.DingTalkService;
import com.iwdnb.bmnf.common.service.CacheService;
import com.iwdnb.bmnf.common.utils.BooleanUtils;
import com.iwdnb.gkgz.application.model.request.AddStrategyTradeStockDataRequest;
import com.iwdnb.gkgz.application.model.vo.HullAverageBiasData;
import com.iwdnb.gkgz.common.model.dto.StrategyTradeDTO;
import com.iwdnb.gkgz.common.quota.BollingerBands;
import com.iwdnb.gkgz.common.quota.BollingerBands.BollingerData;
import com.iwdnb.gkgz.common.quota.HullMovingAverage;
import com.iwdnb.gkgz.common.quota.HullMovingAverage.HullAverageData;
import com.iwdnb.gkgz.application.service.StockRangeService;
import com.iwdnb.gkgz.application.service.StockService;
import com.iwdnb.gkgz.common.constant.CacheConstants;
import com.iwdnb.gkgz.common.enums.RangeTypeEnums;
import com.iwdnb.gkgz.common.model.dto.Stock;
import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.common.model.dto.StockRangeApproximateData;
import com.iwdnb.gkgz.common.model.dto.StockRangeDTO;
import com.iwdnb.gkgz.common.quota.MovingAverage;
import com.iwdnb.gkgz.common.utils.BigDecimalUtils;
import com.iwdnb.gkgz.common.utils.DateUtils;
import com.iwdnb.gkgz.common.utils.MergeDateUtils;
import com.iwdnb.gkgz.common.utils.StockBlackListUtils;
import com.iwdnb.gkgz.common.utils.StockRangeUtils;
import com.iwdnb.gkgz.common.utils.StockTrendUtils;
import com.iwdnb.gkgz.common.utils.StockUtils;
import com.iwdnb.gkgz.infrastructure.dao.repo.StockDayDataRepository;
import com.iwdnb.wwzy.dao.MetaDataDAO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import static com.iwdnb.gkgz.common.constant.GkgzConstants.DEFAULT_START_DATE;
import static com.iwdnb.gkgz.common.constant.GkgzConstants.OBJECT_CODE_STRATEGY_TRADE;
import static com.iwdnb.gkgz.common.utils.StockUtils.caculateAfterDaysProfit;
import static com.iwdnb.gkgz.common.utils.StockUtils.getAveragePrice;

@Service
@Slf4j
public class StockRangeServiceImpl implements StockRangeService {
    @Autowired
    private StockService stockService;
    @Autowired
    private StockDayDataRepository stockDayDataRepository;
    @Autowired
    private CacheService cacheService;
    @Autowired
    private Executor executor;
    @Autowired
    private DingTalkService dingTalkService;
    @Autowired
    private MetaDataDAO metaDataDAO;
    private String DING_TALK_URL
        = "https://oapi.dingtalk"
        + ".com/robot/send?access_token=62fdde2e47cc3876b4723242adfd599c7e7e5712f9952419f1f0be261780b5ea";

    private Map<String, List<StockRangeDTO>> stockRangeMap = new ConcurrentHashMap<>();

    @Override
    public List<StockRangeDTO> queryStockRangeList(String code) {
        String begin = DEFAULT_START_DATE;
        String end = DateUtil.formatDate(new Date());
        List<StockDayData> stockDayDataList = stockDayDataRepository.queryAllByDateRange(code, begin, end);
        return StockRangeUtils.calculateStockRange(stockDayDataList);
    }

    @Override
    public List<StockRangeDTO> queryStockRangeList(String code, boolean useCache) {
        List<StockRangeDTO> stockRangeDTOList = stockRangeMap.get(code);
        if (CollectionUtils.isEmpty(stockRangeDTOList)) {
            stockRangeDTOList = queryStockRangeList(code);
            stockRangeMap.put(code, stockRangeDTOList);
        }
        return stockRangeDTOList;
    }

    @Override
    public List<StockRangeDTO> queryStockRangeList(String code, List<StockDayData> stockDayDataList) {
        return StockRangeUtils.calculateStockRange(stockDayDataList);
    }

    @Override
    public boolean isLowPrice(String code, String date) {
        List<StockRangeDTO> stockRangeDTOList = queryStockRangeList(code);
        List<StockDayData> stockDayDataList = stockDayDataRepository.queryAllByDateRange(code, DEFAULT_START_DATE,
            date);
        StockDayData stockDayData = StockUtils.getStockDayData(stockDayDataList, date);
        return isLowPrice(code, stockDayData, stockRangeDTOList);
    }

    private boolean isLowPrice(String code, StockDayData stockDayData, List<StockRangeDTO> stockRangeDTOList) {
        if (Objects.isNull(stockDayData)) {
            return false;
        }
        //如果与2年内波段最低价近似，确认是最低价
        StockRangeDTO oneYearAgoLowRange = getTwoYearAgoLowRange(stockDayData.getDate(), stockRangeDTOList);
        if (Objects.nonNull(oneYearAgoLowRange)) {
            if (BigDecimalUtils.isLe(stockDayData.getClosePrice(), oneYearAgoLowRange.getLowPrice())) {
                //log.debug("{}-{}与1年内最低价还低,为低价", code, stockDayData.getDate());
                return true;
            }
            BigDecimal rate = BigDecimalUtils.divideToPrecent(stockDayData.getClosePrice(), oneYearAgoLowRange.getLowPrice());
            if (BigDecimalUtils.isLe(rate, new BigDecimal(5))) {
                //log.debug("{}-{}与1年内最低价相差5个点以内,为低价", code, stockDayData.getDate());
                return true;
            }
            //当前股价在1年内最低价的50%以上，不是低价
            if (BigDecimalUtils.isGt(rate, new BigDecimal(50))) {
                log.debug("{}-{}与1年内最低价相差50个点以上,不为低价", code, stockDayData.getDate());
                return false;
            }
        }
        //找到最近似的多频次波段值
        StockRangeApproximateData data = StockRangeUtils.findApproximateDataWithStockRangeList(
            stockDayData.getClosePrice(), stockRangeDTOList, stockDayData.getDate());
        //log.info("{}-{}近似低价波段值为:{}", code, stockDayData.getDate(), JSON.toJSONString(data));
        return Objects.nonNull(data);
    }

    private boolean isHighPrice(String code, StockDayData stockDayData, List<StockRangeDTO> stockRangeDTOList) {
        //如果与2年内波段最高价近似，确认是最高价
        StockRangeDTO twoYearAgoHighRange = getTwoYearAgoHighRange(stockDayData.getDate(), stockRangeDTOList);
        if (Objects.nonNull(twoYearAgoHighRange)) {
            if (BigDecimalUtils.isGe(stockDayData.getClosePrice(), twoYearAgoHighRange.getHighPrice())) {
                //log.debug("{}-{}与2年内最高价还高,为高价", code, stockDayData.getDate());
                return true;
            }
            BigDecimal rate = BigDecimalUtils.subStractAndDividePrecent(stockDayData.getClosePrice(), twoYearAgoHighRange.getHighPrice());
            if (BigDecimalUtils.isLe(rate, new BigDecimal(5))) {
                //log.debug("{}-{}与2年内最高价相差5个点以内,为高价", code, stockDayData.getDate());
                return true;
            }
            //当前股价在2年内最高价的70%以下，不是高价
            if (BigDecimalUtils.isGt(rate, new BigDecimal(30))) {
                //log.debug("{}-{}与2年内最高价相差30个点以上,不为高价", code, stockDayData.getDate());
                return false;
            }
        }
        //找到最近似的多频次波段值
        StockRangeApproximateData data = StockRangeUtils.findHighApproximateDataWithStockRangeList(
            stockDayData.getClosePrice(), stockRangeDTOList, stockDayData.getDate());
        //log.info("{}-{}近似高价波段值为:{}", code, stockDayData.getDate(), JSON.toJSONString(data));
        return Objects.nonNull(data);
    }

    @Override
    public List<String> queryLowPriceDateList(String code) {
        String begin = DEFAULT_START_DATE;
        String end = DateUtil.formatDate(new Date());
        List<StockDayData> stockDayDataList = stockDayDataRepository.queryAllByDateRange(code, begin, end);
        return queryLowPriceDateList(code, stockDayDataList, null);
    }

    @Override
    public List<String> queryLowPriceDateList(String code, List<StockDayData> stockDayDataList, String matchDate) {
        String cacheKey = "lowPriceDates:" + code;
        String dateStr = cacheService.get(cacheKey);
        if (StringUtils.isNotBlank(dateStr)) {
            return Lists.newArrayList(dateStr.split(","));
        }
        List<StockRangeDTO> stockRangeDTOList = StockRangeUtils.calculateStockRange(stockDayDataList);
        List<String> dateList = new ArrayList<>();
        //如果传入日期,就只需要匹配该日期即可
        if (StringUtils.isNotBlank(matchDate)) {
            StockDayData stockDayData = StockUtils.getStockDayData(stockDayDataList, matchDate);
            if (isLowPrice(code, stockDayData, stockRangeDTOList)) {
                dateList.add(stockDayData.getDate());
            }
        } else {
            for (StockDayData stockDayData : stockDayDataList) {
                if (isLowPrice(code, stockDayData, stockRangeDTOList)) {
                    dateList.add(stockDayData.getDate());
                }
            }
        }
        //cacheService.put(cacheKey, StringUtils.join(dateList, ","));
        return dateList;
    }

    @Override
    public List<String> queryBollingLowPriceDateList(String code) {
        String begin = DEFAULT_START_DATE;
        String end = DateUtil.formatDate(new Date());
        List<StockDayData> stockDayDataList = stockDayDataRepository.queryAllByDateRange(code, begin, end);
        return queryBollingLowPriceDateList(code, stockDayDataList, null);
    }

    @Override
    public List<String> queryBollingLowPriceDateList(String code, List<StockDayData> stockDayDataList,
        String matchDate) {
        List<BollingerData> bollingerDataList = BollingerBands.calculateBollingerBands(stockDayDataList);
        List<String> bollingLowDates = new ArrayList<>();
        //如果传入日期,就只需要匹配该日期即可
        if (StringUtils.isNotBlank(matchDate)) {
            StockDayData stockDayData = StockUtils.getStockDayData(stockDayDataList, matchDate);
            BollingerData bollingerData = bollingerDataList.stream().filter(o -> o.getDate().equals(matchDate))
                .findAny().orElse(null);
            if (Objects.nonNull(stockDayData) && Objects.nonNull(bollingerData)
                && BigDecimalUtils.isLt(stockDayData.getClosePrice(), bollingerData.getLowBand())) {
                bollingLowDates.add(matchDate);
            }
        } else {
            for (BollingerData data : bollingerDataList) {
                String date = data.getDate();
                StockDayData dayData = StockUtils.getStockDayData(stockDayDataList, date);
                if (Objects.isNull(data)) {
                    continue;
                }
                if (BigDecimalUtils.isLt(dayData.getClosePrice(), data.getLowBand())) {
                    bollingLowDates.add(date);
                }
            }
        }
        return bollingLowDates;
    }

    public List<String> queryBollingAndLowPriceDateList(String code) {
        String begin = DEFAULT_START_DATE;
        String end = DateUtil.formatDate(new Date());
        List<StockDayData> stockDayDataList = stockDayDataRepository.queryAllByDateRange(code, begin, end);
        return queryBollingAndLowPriceDateList(code, stockDayDataList, null);
    }

    @Override
    public List<String> queryBollingAndLowPriceDateList(String code, List<StockDayData> stockDayDataList,
        String matchDate) {
        List<String> list2 = queryLowPriceDateList(code, stockDayDataList, matchDate);
        List<String> list1 = queryBollingLowPriceDateList(code, stockDayDataList, matchDate);
        List<String> mergeList = MergeDateUtils.mergeDateList(list1, list2, 1);
        return mergeList;
    }

    @Override
    public List<String> queryBollingAndLowPriceAndBiasDateList(String code) {
        String begin = DEFAULT_START_DATE;
        String end = DateUtil.formatDate(new Date());
        return queryBollingAndLowPriceAndBiasDateList(code, begin, end, null);
    }

    @Override
    public List<String> queryBollingAndLowPriceAndBiasDateList(String code, String beginDate,
        String endDate, String matchDate) {
        List<StockDayData> stockDayDataList = stockDayDataRepository.queryAllByDateRange(code, beginDate, endDate);
        if (CollectionUtils.isEmpty(stockDayDataList)) {
            return null;
        }
        List<String> dateList = queryBollingAndLowPriceDateList(code, stockDayDataList, matchDate);
        List<HullAverageBiasData> biasDataList = buildHullAverageBiasDataList(stockDayDataList, dateList);
        dateList = analysisBuyDate(biasDataList, stockDayDataList);
        log.debug("获取乖离率叠加后的买点日期:" + dateList);
        return dateList;
    }

    @Override
    public List<String> queryHighPriceDateList(String code, List<StockDayData> stockDayDataList) {
        String cacheKey = "lowPriceDates:" + code;
        String dateStr = cacheService.get(cacheKey);
        if (StringUtils.isNotBlank(dateStr)) {
            return Lists.newArrayList(dateStr.split(","));
        }
        List<StockRangeDTO> stockRangeDTOList = StockRangeUtils.calculateStockRange(stockDayDataList);
        List<String> dateList = new ArrayList<>();
        for (StockDayData stockDayData : stockDayDataList) {
            if (isHighPrice(code, stockDayData, stockRangeDTOList)) {
                dateList.add(stockDayData.getDate());
            }
        }
        //cacheService.put(cacheKey, StringUtils.join(dateList, ","));
        return dateList;
    }

    @Override
    public List<String> queryBollingHighPriceDateList(String code, List<StockDayData> stockDayDataList) {
        List<BollingerData> bollingerDataList = BollingerBands.calculateBollingerBands(stockDayDataList);
        List<String> bollingLowDates = new ArrayList<>();
        for (BollingerData data : bollingerDataList) {
            String date = data.getDate();
            StockDayData dayData = stockDayDataList.stream().filter(stockDayData -> stockDayData.getDate().equals(date))
                .findFirst().orElse(null);
            if (Objects.isNull(data)) {
                continue;
            }
            if (BigDecimalUtils.isGt(dayData.getClosePrice(), data.getHighBand())) {
                bollingLowDates.add(date);
            }
        }
        return bollingLowDates;
    }

    @Override
    public List<String> queryBollingAndHighPriceDateList(String code, List<StockDayData> stockDayDataList) {
        List<String> list1 = queryBollingHighPriceDateList(code, stockDayDataList);
        List<String> list2 = queryHighPriceDateList(code, stockDayDataList);
        List<String> mergeList = MergeDateUtils.mergeDateList(list1, list2, 1);
        return mergeList;
    }

    @Override
    public List<String> queryBollingAndHighPriceAndBiasDateList(String code) {
        String begin = DEFAULT_START_DATE;
        String end = DateUtil.formatDate(new Date());
        List<StockDayData> stockDayDataList = stockDayDataRepository.queryAllByDateRange(code, begin, end);
        if (CollectionUtils.isEmpty(stockDayDataList)) {
            return null;
        }
        List<String> dateList = queryBollingAndHighPriceDateList(code, stockDayDataList);
        List<HullAverageBiasData> biasDataList = buildHullAverageBiasDataList(stockDayDataList, dateList);
        dateList = analysisSellDate(biasDataList, stockDayDataList);
        //log.debug("获取乖离率叠加后的卖点日期:" + dateList);
        return dateList;
    }

    @Override
    public List<String> queryTodayBuyStockList(String date) {
        Date d = StringUtils.isNotBlank(date) ? DateUtil.parseDate(date) : new Date();
        String begin = DateUtil.formatDate(DateUtil.offsetMonth(d, -24));
        String end = DateUtil.formatDate(d);
        List<Stock> stockList = stockService.queryStockList();
        List<String> codes = StockUtils.formatStockCodes(stockList);
        codes = StockBlackListUtils.getCanBuyCodeList(codes);
        List<String> matchCodes = new CopyOnWriteArrayList<>();
        String lowDate = DateUtil.formatDate(DateUtil.offsetDay(d, -1));
        boolean endFlag = false;
        for (int i = 0; i < codes.size(); i++) {
            String code = codes.get(i);
            if (i == codes.size() - 1) {
                endFlag = true;
            }
            boolean finalEndFlag = endFlag;
            int index = i;
            executor.execute(() -> {
                List<String> matchDateList = queryBollingAndLowPriceAndBiasDateList(code, begin, end, lowDate);
                if (CollectionUtils.isNotEmpty(matchDateList) && matchDateList.contains(date)) {
                    log.info("{}-{}匹配买点", code, index);
                    String cacheKey = CacheConstants.LOW_STOCK_BUY + date + ":" + code;
                    cacheService.put(cacheKey, "y", 86400);
                    matchCodes.add(code);
                } else {
                    log.info("{}-{}未匹配买点,忽略", code, index);
                }
                if (finalEndFlag) {
                    try {
                        Thread.sleep(30000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    String message = CollectionUtils.isEmpty(matchCodes) ? date + "没有可买入股票"
                        : date + "符合今日买点:" + StringUtils.join(matchCodes, ",");
                    dingTalkService.sendMessage(DING_TALK_URL,
                        "股票通知:" + message,
                        Lists.newArrayList("18057138912"));
                }
            });
        }
        return matchCodes;
    }

    @Override
    public List<String> queryLowStockList(String date) {
        Date d = StringUtils.isNotBlank(date) ? DateUtil.parseDate(date) : new Date();
        String begin = DateUtil.formatDate(DateUtil.offsetMonth(d, -24));
        String end = DateUtil.formatDate(d);
        List<Stock> stockList = stockService.queryStockList();
        List<String> codes = StockUtils.formatStockCodes(stockList);
        codes = StockBlackListUtils.getCanBuyCodeList(codes);
        List<String> matchCodes = new CopyOnWriteArrayList<>();
        boolean endFlag = false;
        for (int i = 0; i < codes.size(); i++) {
            String code = codes.get(i);
            if (i == codes.size() - 1) {
                endFlag = true;
            }
            boolean finalEndFlag = endFlag;
            executor.execute(() -> {
                List<StockDayData> stockDayDataList = stockDayDataRepository.queryAllByDateRange(code, begin, end);
                if (CollectionUtils.isNotEmpty(stockDayDataList)) {
                    List<String> dateList = queryBollingAndLowPriceDateList(code, stockDayDataList, date);
                    log.info("{}-{}低价信息:{}", date, code, dateList);
                    if (CollectionUtils.isNotEmpty(dateList)) {
                        String cacheKey = CacheConstants.LOW_STOCK_PRICE_CACHE_PREFIX + date + ":" + code;
                        cacheService.put(cacheKey, "y", 86400);
                        matchCodes.add(code);
                    }
                }
                if (finalEndFlag) {
                    try {
                        Thread.sleep(30000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    String message = CollectionUtils.isEmpty(matchCodes) ? date + "没有低价股股票"
                        : date + "低价股:" + StringUtils.join(matchCodes, ",");
                    dingTalkService.sendMessage(DING_TALK_URL,
                        "股票通知:" + message,
                        Lists.newArrayList("18057138912"));
                }
            });
        }
        return matchCodes;
    }

    @Override
    public String getTrend(String code, String date, int period) {
        List<StockDayData> stockDayDataList = stockDayDataRepository.queryAllBeforeDate(code, date);
        StockDayData data = StockUtils.getStockDayData(stockDayDataList, date);
        String trend = StockTrendUtils.getTrend(data, stockDayDataList, period);
        return trend;
    }

    /**
     * 以下为：20日线买入策略
     * 以跌破20日线为起始日，3日内反弹回20日线的当天为买入信号,买入信号的价格当天在20日线上方5个点以内
     * 买入日前5天不能出现跌破20日线
     * 30日线、60日线均线向上
     * 20日均线向上，20天之前和跌破20日线当天的价差为10%以上
     * 反弹回20日线的成交量与跌破20日线前5日平均成交量基本持平，可以比平均成交量大，最小不能小于平均量的80%
     *
     *
     * 止盈止损
     * 最多持股5日
     * 跌破买入当天5个点，止损
     * 买入连跌2天，止损
     * 3日内未涨到3个点以上，止盈
     * 3日内超过10个点，止盈,类一字板除外(开盘价和收盘价都是涨停)
     * 5日内超过15个点,止盈，类一字板除外(开盘价和收盘价都是涨停)
     * 价格到达10内最高价时，自动卖出
     *
     * @param code
     * @return
     */
    @Override
    public List<String> queryTrendTwentyBackDateList(String code) {
        List<StockDayData> stockDayDataList = stockDayDataRepository.queryAll(code);
        return queryTrendTwentyBackDateList(code, stockDayDataList);
    }

    @Override
    public List<String> queryTrendTwentyBackDateList(String code, List<StockDayData> stockDayDataList) {
        int twentyPeriod = 20;
        int thirtyPeriod = 30;
        int sixtyPeriod = 60;
        MovingAverage.calculateEMA(stockDayDataList, 20);
        MovingAverage.calculateEMA(stockDayDataList, 30);
        MovingAverage.calculateEMA(stockDayDataList, 60);
        List<String> dateList = new ArrayList<>();
        for (int i = 0; i < stockDayDataList.size(); i++) {
            if (i < 60) {
                continue;
            }
            StockDayData data = stockDayDataList.get(i);
            String date = data.getDate();
            BigDecimal closePrice = data.getClosePrice();
            BigDecimal twentyAvg = getAveragePrice(data, twentyPeriod);
            //收盘价高于20日均线，跳过
            if (BigDecimalUtils.isGe(closePrice, twentyAvg)) {
                //log.debug("{}-{}收盘价高于20日均线，跳过", code, date);
                continue;
            }
            ////最低价要比20日均价小3个点以内,跳过
            //BigDecimal minRange = twentyAvg.subtract(data.getMinPrice());
            //BigDecimal minRate = BigDecimalUtils.divideToPrecent(minRange, twentyAvg);
            //if (BigDecimalUtils.isLt(minRate, new BigDecimal(3))) {
            //    //log.debug("{}-{}收盘价高于20日均线，跳过", code, date);
            //    continue;
            //}
            //前一天收盘价在20日均线下方，跳过
            StockDayData yesterdayData = stockDayDataList.get(i - 1);
            BigDecimal yesterdayClosePrice = yesterdayData.getClosePrice();
            BigDecimal yesterdayTwentyAvg = getAveragePrice(yesterdayData, twentyPeriod);
            if (BigDecimalUtils.isLe(yesterdayClosePrice, yesterdayTwentyAvg)) {
                //log.debug("{}-{}前一天收盘价在20日均线下方，跳过", code, date);
                continue;
            }
            //买入信号日前10日内出现过跌破20日线的2次以上，跳过
            List<StockDayData> beforeTenDayDatas = StockUtils.getStockDayDataListBeforeDate(stockDayDataList, date,
                10);
            int repeatSignalCount = 0;
            for (StockDayData subData : beforeTenDayDatas) {
                BigDecimal subMinPrice = subData.getMinPrice();
                BigDecimal subTwentyAvg = getAveragePrice(subData, twentyPeriod);
                if (BigDecimalUtils.isLe(subMinPrice, subTwentyAvg)) {
                    repeatSignalCount++;
                    break;
                }
            }
            if (repeatSignalCount > 1) {
                continue;
            }
            //5日前的20日均价比当天低4%以上，10日前的20日均价比当天低10%以上
            List<StockDayData> beforeFiveDayDatas = StockUtils.getStockDayDataListBeforeDate(stockDayDataList, date,
                5);
            StockDayData beforeFiveData = beforeFiveDayDatas.get(0);
            BigDecimal beforeFiveAvg = getAveragePrice(beforeFiveData, 20);
            BigDecimal beforeFiveRange = twentyAvg.subtract(beforeFiveAvg);
            BigDecimal beforeFiveRate = BigDecimalUtils.divideToPrecent(beforeFiveRange, beforeFiveAvg);
            //20日前的20日均价比当天低10%以内，跳过
            if (BigDecimalUtils.isLt(beforeFiveRate, new BigDecimal(3))) {
                //log.debug("{}-{}20日前的20日均价比当天低10%以内，跳过", code, date);
                continue;
            }
            StockDayData beforeTenData = beforeTenDayDatas.get(0);
            BigDecimal beforeTenAvg = getAveragePrice(beforeTenData, 20);
            BigDecimal beforeTenRange = twentyAvg.subtract(beforeTenAvg);
            BigDecimal beforeTenRate = BigDecimalUtils.divideToPrecent(beforeTenRange, beforeTenAvg);
            //10日前的20日均价比当天低3%以内，跳过
            if (BigDecimalUtils.isLt(beforeTenRate, new BigDecimal(10))) {
                //log.debug("{}-{}10日前的20日均价比当天低3%以内，跳过", code, date);
                continue;
            }
            String twentyTrend = StockTrendUtils.getTrend(data, stockDayDataList, thirtyPeriod);
            String thirtyTrend = StockTrendUtils.getTrend(data, stockDayDataList, thirtyPeriod);
            String sixtyTrend = StockTrendUtils.getTrend(data, stockDayDataList, sixtyPeriod);
            BigDecimal thirtyAvg = getAveragePrice(data, thirtyPeriod);
            BigDecimal sixtyAvg = getAveragePrice(data, sixtyPeriod);
            //a.20、30、60日线均向上，且价格依次排布
            Boolean trendFlag = RangeTypeEnums.UP.equalsCode(twentyTrend) && RangeTypeEnums.UP.equalsCode(thirtyTrend)
                && RangeTypeEnums.UP.equalsCode(sixtyTrend)
                && BigDecimalUtils.isGe(twentyAvg, thirtyAvg) && BigDecimalUtils.isGe(thirtyAvg, sixtyAvg);
            if (!trendFlag) {
                //log.debug("{}-{}趋势向下，跳过", code, date);
                continue;
            }
            //3日内有反弹回20日线上方，且不超过5个点
            List<StockDayData> afterThreeDayDatas = StockUtils.getStockDayDataListAfterDate(stockDayDataList, date, 3);
            boolean backFlag = false;
            StockDayData backDayData = null;
            BigDecimal subOpenPrice = data.getClosePrice();
            for (StockDayData subData : afterThreeDayDatas) {
                BigDecimal subClosePrice = subData.getClosePrice();
                BigDecimal subTwentyAvg = getAveragePrice(subData, twentyPeriod);
                BigDecimal subLowPrice = BigDecimalUtils.isGt(subClosePrice, subData.getOpenPrice())
                    ? subData.getOpenPrice() : subClosePrice;
                //BigDecimal subLowRange = subLowPrice.subtract(subData.getMinPrice());
                //BigDecimal subLowRate = BigDecimalUtils.divideToPrecent(subLowRange, subData.getMinPrice());
                //if(BigDecimalUtils.isGt(subLowRate,new BigDecimal(3))){
                //    backFlag = true;
                //    backDayData = subData;
                //    break;
                //}else
                if (BigDecimalUtils.isGt(subClosePrice, subTwentyAvg)) {
                    //如果收盘价不超过20均线5个点
                    BigDecimal subRange = subClosePrice.subtract(subTwentyAvg);
                    BigDecimal subRate = BigDecimalUtils.divideToPrecent(subRange, subTwentyAvg);
                    //log.debug("{}-{}反弹超过20日均线", code, subDate);
                    if (BigDecimalUtils.isLt(subRate, new BigDecimal(3))) {
                        //log.debug("{}-{}反弹未超过3个点,反弹成立", code, subDate);
                        backFlag = true;
                        backDayData = subData;
                        break;
                    }
                }
            }
            //如果没有反弹,直接跳过
            if (!backFlag) {
                //log.debug("{}-{}没有反弹，跳过", code, date);
                continue;
            }
            //反弹日上影线大于3个点,跳过
            BigDecimal backDayUpRange = backDayData.getMaxPrice().subtract(backDayData.getClosePrice());
            BigDecimal backDayUpRate = BigDecimalUtils.divideToPrecent(backDayUpRange, backDayData.getClosePrice());
            if (BigDecimalUtils.isGt(backDayUpRate, new BigDecimal(3))) {
                continue;
            }
            //反弹日之前13天的最低价,应该是在买入日之后
            List<StockDayData> beforeFifteenDatas = StockUtils.getStockDayDataListBeforeDate(stockDayDataList,
                backDayData.getDate(), 15);
            BigDecimal minPrice = beforeFifteenDatas.get(0).getMinPrice();
            String minDate = beforeFifteenDatas.get(0).getDate();
            for (StockDayData subData : beforeFifteenDatas) {
                BigDecimal subMinPrice = subData.getMinPrice();
                if (BigDecimalUtils.isLt(subMinPrice, minPrice)) {
                    minPrice = subMinPrice;
                    minDate = subData.getDate();
                }
            }
            //最小价格日出现在买入信号之前,跳过
            if (DateUtil.parseDate(date).getTime() > DateUtil.parseDate(minDate).getTime()) {
                continue;
            }
            //成交量近似
            List<StockDayData> beforeFourDayDatas = StockUtils.getStockDayDataListBeforeDate(stockDayDataList, date, 4);
            beforeFourDayDatas.add(data);
            int tradeNumSum = beforeFourDayDatas.stream().collect(Collectors.summingInt(o -> o.getTradeNum()));
            double tradeNum = (tradeNumSum / 5.0) * 0.8;
            tradeNum = tradeNum > data.getTradeNum() ? data.getTradeNum() : tradeNum;
            //反弹日成交量不到平均成交量的8折,跳过
            if (tradeNum > backDayData.getTradeNum()) {
                //log.debug("{}-{}反弹日成交量不到平均成交量的8折，跳过", code, date);
                continue;
            }
            String buyDate = backDayData.getDate();
            if (!dateList.contains(buyDate)) {
                dateList.add(buyDate);
                //BigDecimal profit = caculateAfterDaysProfit(backDayData.getDate(), stockDayDataList, 5);
                //log.debug("{}-{}买入,未来5天最高收益为:{}", code, buyDate, profit);
            }

        }
        return dateList;
    }

    @Override
    public void doAllTrendTwentyBackTest(AddStrategyTradeStockDataRequest request) {
        List<Stock> stockList = stockService.queryStockList();
        String codeStr = request.getCodes();
        List<String> codes = null;
        if (StringUtils.isNotBlank(codeStr)) {
            codes = Lists.newArrayList(codeStr.split(","));
        } else {
            codes = stockList.stream()
                .map(stock -> stock.getCode()).collect(Collectors.toList());
            codes = StockBlackListUtils.getCanBuyCodeList(codes);
            int end = request.getEnd() > codes.size() ? codes.size() : request.getEnd();
            codes = codes.subList(request.getBegin(), end);
        }
        for (int i = 0; i < codes.size(); i++) {
            if (i % 100 == 0) {
                log.info("doAllTrendTwentyBackTest index:{}", i);
            }
            String code = codes.get(i);
            executor.execute(() -> {
                doTrendTwentyBackTest(code, request.getBackFlag());
            });
        }
    }

    /**
     * 止盈止损
     * 最多持股5日
     * 跌破买入当天5个点，止损
     * 买入连跌2天，止损
     * 3日内未涨到3个点以上，止盈
     * 3日内超过10个点，止盈,类一字板除外(开盘价和收盘价都是涨停)
     * 5日内超过15个点,止盈，类一字板除外(开盘价和收盘价都是涨停)
     * 价格到达10内最高价时，自动卖出
     *
     * @param code
     * @param backFlag
     */
    @Override
    public void doTrendTwentyBackTest(String code, String backFlag) {
        List<StockDayData> stockDayDataList = stockDayDataRepository.queryAll(code);
        if (CollectionUtils.isEmpty(stockDayDataList)) {
            return;
        }
        List<String> dateList = queryTrendTwentyBackDateList(code, stockDayDataList);
        if (CollectionUtils.isEmpty(dateList)) {
            //log.debug("{}-没有可以买入的日期,跳过", code);
            return;
        }
        log.debug("{}-匹配日期:{}", code, dateList);
        if (BooleanUtils.isFalse(backFlag)) {
            return;
        }
        Date latestEndDate = DateUtil.parseDate(dateList.get(0));
        latestEndDate = DateUtil.offsetDay(latestEndDate, -1);
        List<StrategyTradeDTO> strategyTradeDTOList = new ArrayList<>();
        for (String date : dateList) {
            Date d = DateUtil.parseDate(date);
            if (DateUtils.beforeOrEquals(d, latestEndDate)) {
                log.debug("{}-{}在上一个周期内,忽略", code, date);
                continue;
            }
            StrategyTradeDTO strategyTradeDTO = getTwentyBackTradeInfo(code, stockDayDataList, date);
            if (Objects.nonNull(strategyTradeDTO)) {
                latestEndDate = strategyTradeDTO.getSellDate();
                strategyTradeDTOList.add(strategyTradeDTO);
            }
        }
        if (CollectionUtils.isNotEmpty(strategyTradeDTOList)) {
            metaDataDAO.insert(OBJECT_CODE_STRATEGY_TRADE, strategyTradeDTOList);
        }

    }

    private StrategyTradeDTO getTwentyBackTradeInfo(String code, List<StockDayData> stockDayDataList, String date) {
        StockDayData buyDayData = StockUtils.getStockDayData(stockDayDataList, date);
        BigDecimal buyPrice = buyDayData.getClosePrice();
        List<StockDayData> subList = StockUtils.getStockDayDataListAfterDate(stockDayDataList, date, 5);
        if (CollectionUtils.isEmpty(subList) || subList.size() < 5) {
            return null;
        }
        BigDecimal rate = null;
        String sellDate = null;
        String info = null;
        BigDecimal sellPrice = null;
        int holdIndex = 0;
        for (int i = 0; i < 5; i++) {
            StockDayData data = subList.get(i);
            BigDecimal closePrice = data.getClosePrice();
            BigDecimal openPrice = data.getOpenPrice();
            BigDecimal range = BigDecimalUtils.subtract(closePrice, buyPrice);
            rate = BigDecimalUtils.divideToPrecent(range, buyPrice);
            sellDate = data.getDate();
            sellPrice = data.getClosePrice();
            holdIndex = i;
            //上影线大于3个点,卖出
            BigDecimal upPrice = BigDecimalUtils.isGt(openPrice, closePrice) ? openPrice : closePrice;
            BigDecimal upRange = data.getMaxPrice().subtract(upPrice);
            BigDecimal upRate = BigDecimalUtils.divideToPrecent(upRange, upPrice);
            if (BigDecimalUtils.isGt(upRate, new BigDecimal(3)) && i < 2) {
                log.debug("{}-{}-上影线大于3个点，卖出，收益={}", code, data.getDate(), rate);
                info = "上影线大于3个点，卖出";
                break;
            }
            //亏损大于5个点，卖出
            if (BigDecimalUtils.isLe(rate, new BigDecimal(-5))) {
                log.debug("{}-{}-亏损大于5个点，卖出，收益={}", code, data.getDate(), rate);
                info = "亏损大于5个点，卖出";
                break;
            }
            //第一天亏损大于3个点，卖出
            if (i == 0 && BigDecimalUtils.isLe(rate, new BigDecimal(-3))) {
                log.debug("{}-{}-第一天亏损大于3个点，卖出，收益={}", code, data.getDate(), rate);
                info = "第一天亏损大于3个点，卖出";
                break;
            }
            BigDecimal dayRange = BigDecimalUtils.subtract(closePrice, openPrice);
            BigDecimal dayRate = BigDecimalUtils.divideToPrecent(dayRange, openPrice);
            //单日亏损大于5个点，卖出
            if (BigDecimalUtils.isLe(dayRate, new BigDecimal(-5))) {
                log.debug("{}-{}-单日亏损大于5个点，卖出，收益={}", code, data.getDate(), rate);
                info = "亏损大于5个点，卖出";
                break;
            }
            //3天内的收益小于3个点，卖出
            if (i == 2 && BigDecimalUtils.isLt(rate, new BigDecimal(3))) {
                log.debug("{}-{}-3天内的收益小于3个点，卖出，收益={}", code, data.getDate(), rate);
                info = "3天内的收益小于3个点，卖出";
                break;
            }
            if (i == 1) {
                BigDecimal firstDayPrice = subList.get(0).getClosePrice();
                if (BigDecimalUtils.isLt(firstDayPrice, buyPrice)
                    && BigDecimalUtils.isLt(data.getClosePrice(), firstDayPrice)) {
                    log.debug("{}-{}-连续2天下跌，卖出，收益={}", code, data.getDate(), rate);
                    info = "连续2天下跌，卖出";
                    break;
                }
            }
            //3天内的收益大于15个点，卖出
            if (BigDecimalUtils.isGt(rate, new BigDecimal(15)) && i <= 2) {
                //类一字板，跳过
                if (isOneLineStock(data)) {
                    continue;
                }
                log.debug("{}-{}-3天内的收益大于15个点，卖出，收益={}", code, data.getDate(), rate);
                info = "3天内的收益大于10个点，卖出";
                break;
            }

            //5天内的收益大于25个点，卖出
            if (BigDecimalUtils.isGe(rate, new BigDecimal(25))) {
                //类一字板，跳过
                if (isOneLineStock(data)) {
                    continue;
                }
                log.debug("{}-{}-5天内的收益大于15个点，卖出，收益={}", code, data.getDate(), rate);
                info = "5天内的收益大于15个点，卖出";
                break;
            }
            if (i == 4) {
                log.debug("{}-{}-第5天自动卖出，卖出，收益={}", code, data.getDate(), rate);
                info = "第5天自动卖出";
            }
        }
        StrategyTradeDTO strategyTradeDTO = new StrategyTradeDTO();
        strategyTradeDTO.setCode(code);
        strategyTradeDTO.setUuid("trendTwentyBack");
        strategyTradeDTO.setHoldDay(holdIndex + 1);
        strategyTradeDTO.setBuyDate(DateUtil.parseDate(buyDayData.getDate()));
        strategyTradeDTO.setBuyPrice(buyDayData.getClosePrice());
        strategyTradeDTO.setSellDate(DateUtil.parseDate(sellDate));
        strategyTradeDTO.setSellPrice(sellPrice);
        strategyTradeDTO.setProfitRate(rate);
        strategyTradeDTO.setStrategyDescription(info);
        strategyTradeDTO.setStatus("sell");
        strategyTradeDTO.setWinFlag(BigDecimalUtils.isGt(rate, BigDecimal.ZERO) ? "y" : "n");
        return strategyTradeDTO;
    }

    private boolean isOneLineStock(StockDayData data) {
        BigDecimal openPrice = data.getOpenPrice();
        BigDecimal closePrice = data.getClosePrice();
        BigDecimal range = BigDecimalUtils.subtract(closePrice, openPrice).abs();
        BigDecimal rate = BigDecimalUtils.divideToPrecent(range, openPrice);
        if (BigDecimalUtils.isLt(rate, new BigDecimal(0.5))) {
            return true;
        }
        return false;
    }

    private List<HullAverageBiasData> buildHullAverageBiasDataList(List<StockDayData> stockDayDataList,
        List<String> dateList) {
        List<HullAverageData> averageDataList = HullMovingAverage.calculateHMA(stockDayDataList, 5);
        List<HullAverageData> middleAverageDataList = HullMovingAverage.calculateHMA(stockDayDataList, 20);
        List<HullAverageData> longAverageDataList = HullMovingAverage.calculateHMA(stockDayDataList, 30);
        List<HullAverageBiasData> biasDataList = new ArrayList<>();
        for (String date : dateList) {
            HullAverageBiasData biasData = buildHullAverageBiasData(date, stockDayDataList, averageDataList,
                middleAverageDataList, longAverageDataList, true);
            biasDataList.add(biasData);
            StockDayData nextDayData = getNextStockDayData(date, stockDayDataList);
            if (Objects.nonNull(nextDayData) && !dateList.contains(nextDayData.getDate())) {
                HullAverageBiasData nextDaybiasData = buildHullAverageBiasData(nextDayData.getDate(), stockDayDataList,
                    averageDataList, middleAverageDataList, longAverageDataList, false);
                biasDataList.add(nextDaybiasData);
            }
        }
        return biasDataList;
    }

    private List<String> analysisBuyDate(List<HullAverageBiasData> biasDataList, List<StockDayData> stockDayDataList) {
        List<String> dateList = new ArrayList<>();
        List<List<HullAverageBiasData>> list = new ArrayList<>();
        List<HullAverageBiasData> subList = new ArrayList<>();
        for (HullAverageBiasData biasData : biasDataList) {
            subList.add(biasData);
            if (!biasData.isLowOrHighPriceFlag()) {
                list.add(subList);
                subList = new ArrayList<>();
            }
        }
        BigDecimal fivePercent = new BigDecimal(5);
        BigDecimal twoPercent = new BigDecimal(2);
        for (List<HullAverageBiasData> sub : list) {
            if (CollectionUtils.isEmpty(sub)) {
                continue;
            }
            HullAverageBiasData hullAverageBiasData = sub.get(0);
            if (Objects.isNull(hullAverageBiasData.getAverageData()) || Objects.isNull(
                hullAverageBiasData.getLongAverageData())) {
                continue;
            }
            BigDecimal shortBias = sub.get(0).getAverageData().getBias();
            BigDecimal longBias = sub.get(0).getLongAverageData().getBias();
            boolean invertShort = false;
            boolean invertLong = false;
            for (HullAverageBiasData data : sub) {
                BigDecimal shortRange = BigDecimalUtils.subtract(data.getAverageData().getBias(), shortBias);
                if (BigDecimalUtils.isGe(shortRange, fivePercent)) {
                    //log.debug("{}-短周期趋势反转,幅度:{}", data.getDate(), shortRange);
                    invertShort = true;
                }
                BigDecimal longRange = BigDecimalUtils.subtract(data.getLongAverageData().getBias(), longBias);
                if (BigDecimalUtils.isGe(longRange, twoPercent)) {
                    //log.debug("{}-长周期趋势反转,幅度:{}", data.getDate(), longRange);
                    invertLong = true;
                }
                shortBias = data.getAverageData().getBias();
                longBias = data.getLongAverageData().getBias();
                //长短周期均线均反转,表示趋势反转,可买入
                if (invertShort && invertLong) {
                    //BigDecimal profit = caculateAfterDaysProfit(stockDayDataList.get(0).getCode(), data.getDate(),
                    //    stockDayDataList, 10);
                    //log.debug("{}-长短周期趋势反转,买入,未来10天的收益:{}", data.getDate(), profit);
                    invertShort = false;
                    invertLong = false;
                    dateList.add(data.getDate());
                }
            }
        }
        return dateList;
    }

    private List<String> analysisSellDate(List<HullAverageBiasData> biasDataList, List<StockDayData> stockDayDataList) {
        List<String> dateList = new ArrayList<>();
        List<List<HullAverageBiasData>> list = new ArrayList<>();
        List<HullAverageBiasData> subList = new ArrayList<>();
        for (HullAverageBiasData biasData : biasDataList) {
            subList.add(biasData);
            if (!biasData.isLowOrHighPriceFlag()) {
                list.add(subList);
                subList = new ArrayList<>();
            }
        }
        BigDecimal fivePercent = new BigDecimal(5);
        BigDecimal twoPercent = new BigDecimal(2);
        for (List<HullAverageBiasData> sub : list) {
            if (CollectionUtils.isEmpty(sub)) {
                continue;
            }
            HullAverageBiasData hullAverageBiasData = sub.get(0);
            if (Objects.isNull(hullAverageBiasData.getAverageData()) || Objects.isNull(
                hullAverageBiasData.getLongAverageData())) {
                continue;
            }
            BigDecimal shortBias = sub.get(0).getAverageData().getBias();
            BigDecimal longBias = sub.get(0).getLongAverageData().getBias();
            boolean invertShort = false;
            boolean invertLong = false;
            for (HullAverageBiasData data : sub) {
                //log.debug("{}-短周期bias:{},长周期bias:{}", data.getDate(), data.getAverageData().getBias(),
                //    data.getLongAverageData().getBias());
                BigDecimal shortRange = BigDecimalUtils.subtract(shortBias, data.getAverageData().getBias());
                if (BigDecimalUtils.isGe(shortRange, fivePercent)) {
                    //log.debug("{}-短周期趋势反转,幅度:{},当日bias:{}", data.getDate(), shortRange,
                    //    data.getAverageData().getBias());
                    invertShort = true;
                }
                BigDecimal longRange = BigDecimalUtils.subtract(longBias, data.getLongAverageData().getBias());
                if (BigDecimalUtils.isGe(longRange, twoPercent)) {
                    //log.debug("{}-长周期趋势反转,幅度:{},当日bias:{}", data.getDate(), longRange,
                    //    data.getLongAverageData().getBias());
                    invertLong = true;
                }
                shortBias = data.getAverageData().getBias();
                longBias = data.getLongAverageData().getBias();
                //长短周期均线均反转,表示趋势反转,可卖出
                if (invertShort && invertLong) {
                    //BigDecimal profit = caculateAfterDaysProfit(stockDayDataList.get(0).getCode(), data.getDate(),
                    //    stockDayDataList, 10);
                    //log.debug("{}-长短周期趋势反转,卖出,未来10天的收益:{}", data.getDate(), profit);
                    invertShort = false;
                    invertLong = false;
                    dateList.add(data.getDate());
                }
            }
        }
        return dateList;
    }

    private HullAverageBiasData buildHullAverageBiasData(String date, List<StockDayData> stockDayDataList,
        List<HullAverageData> averageDataList, List<HullAverageData> middleAverageDataList,
        List<HullAverageData> longAverageDataList, boolean isLowPrice) {
        HullAverageBiasData data = new HullAverageBiasData();
        data.setLowOrHighPriceFlag(isLowPrice);
        StockDayData stockDayData = stockDayDataList.stream().filter(o -> o.getDate().equals(date)).findAny()
            .orElse(null);
        data.setDate(date);
        if (Objects.nonNull(stockDayData)) {
            data.setPrice(stockDayData.getClosePrice());
        }
        HullAverageData averageData = averageDataList.stream().filter(
                hullAverageData -> hullAverageData.getDate().equals(data.getDate()))
            .findAny().orElse(null);
        if (Objects.nonNull(averageData)) {
            data.setAverageData(averageData);
        }
        HullAverageData middleAverageData = averageDataList.stream().filter(
                hullAverageData -> hullAverageData.getDate().equals(data.getDate()))
            .findAny().orElse(null);
        if (Objects.nonNull(middleAverageData)) {
            data.setMiddleAverageData(middleAverageData);
        }
        HullAverageData longAverageData = longAverageDataList.stream().filter(
                hullAverageData -> hullAverageData.getDate().equals(data.getDate()))
            .findAny().orElse(null);
        if (Objects.nonNull(longAverageData)) {
            data.setLongAverageData(longAverageData);
        }
        return data;
    }

    private StockDayData getNextStockDayData(String date, List<StockDayData> stockDayDataList) {
        for (int i = 0; i < stockDayDataList.size(); i++) {
            StockDayData data = stockDayDataList.get(i);
            if (data.getDate().equals(date) && i < stockDayDataList.size() - 1) {
                return stockDayDataList.get(i + 1);
            }
        }
        return null;
    }

    /**
     * 获取最近2年的最低价
     *
     * @param lowRangeList
     * @return
     */
    private StockRangeDTO getTwoYearAgoLowRange(String dateStr, List<StockRangeDTO> lowRangeList) {
        //找到一年内最低价波段
        Date date = DateUtil.parseDate(dateStr);
        Date oneYearBefore = DateUtil.offsetDay(date, -720);
        lowRangeList = lowRangeList.stream().filter(stockRangeDTO -> {
            if (stockRangeDTO.getRangeType().equals(RangeTypeEnums.DOWN.getCode())) {
                return false;
            }
            Date rangeDate = DateUtil.parseDate(stockRangeDTO.getBeginDate());
            return rangeDate.after(oneYearBefore) && rangeDate.before(date);
        }).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(lowRangeList)) {
            return null;
        }
        StockRangeDTO low = lowRangeList.get(0);
        for (StockRangeDTO stockRangeDTO : lowRangeList) {
            //上涨波段,最低价为波段开始价格
            if (BigDecimalUtils.isLt(stockRangeDTO.getLowPrice(), low.getLowPrice())) {
                low = stockRangeDTO;
            }
        }
        return low;
    }

    /**
     * 获取最近2年的最高价
     *
     * @param rangeList
     * @return
     */
    private StockRangeDTO getTwoYearAgoHighRange(String dateStr, List<StockRangeDTO> rangeList) {
        //找到2年内最高价波段
        Date date = DateUtil.parseDate(dateStr);
        Date twoYearBefore = DateUtil.offsetDay(date, -720);
        rangeList = rangeList.stream().filter(stockRangeDTO -> {
            if (stockRangeDTO.getRangeType().equals(RangeTypeEnums.UP.getCode())) {
                return false;
            }
            Date rangeDate = DateUtil.parseDate(stockRangeDTO.getBeginDate());
            return rangeDate.after(twoYearBefore) && rangeDate.before(date);
        }).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(rangeList)) {
            return null;
        }
        StockRangeDTO rangeDTO = rangeList.get(0);
        for (StockRangeDTO stockRangeDTO : rangeList) {
            //下跌波段,最高价为波段开始价格
            if (BigDecimalUtils.isGt(stockRangeDTO.getHighPrice(), rangeDTO.getHighPrice())) {
                rangeDTO = stockRangeDTO;
            }
        }
        return rangeDTO;
    }

    private StockRangeDTO getHighRange(List<StockRangeDTO> rangeList) {
        StockRangeDTO high = rangeList.get(0);
        for (StockRangeDTO stockRangeDTO : rangeList) {
            //下跌波段,最高价为波段开始价格
            if (BigDecimalUtils.isGt(stockRangeDTO.getHighPrice(), high.getHighPrice())) {
                high = stockRangeDTO;
            }
        }
        return high;
    }

    /**
     * 找到最近价值
     *
     * @param lst    列表
     * @param target 输入
     * @return {@link String}
     */
    public static StockRangeDTO findNearestRange(List<StockRangeDTO> lst, BigDecimal target) {
        try {
            double targetVlue = target.doubleValue();
            double nearestValue = lst.get(0).getLowPrice().doubleValue();
            double diff = Math.abs(nearestValue - targetVlue);
            StockRangeDTO nearestRange = lst.get(0);
            for (int i = 1; i < lst.size(); i++) {
                double value = lst.get(i).getLowPrice().doubleValue();
                double newDiff = Math.abs(value - targetVlue);
                if (newDiff < diff && targetVlue > value) {
                    diff = newDiff;
                    nearestRange = lst.get(i);
                }
            }
            return nearestRange;
        } catch (Exception e) {
            return null;
        }
    }

}
